package com.yuandian.base.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuandian.api.management.feign.RemoteCommonInterfaceService;
import com.yuandian.base.service.IScriptService;
import com.yuandian.bpm.admin.api.entity.SysDictItem;
import com.yuandian.bpm.admin.api.feign.RemoteDictService;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.base.mapper.CommonMapper;
import com.yuandian.po.CommonConf;
import com.yuandian.po.MergeDataConfig;
import com.yuandian.po.ValidationRule;
import com.yuandian.resolver.IsNotEmptyResolver;
import com.yuandian.resolver.DollarResolver;
import com.yuandian.base.service.ICommonService;
import com.yuandian.utils.MergeDataUtil;
import com.yuandian.utils.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.PropertyPlaceholderHelper;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 说明：通用接口实现
 * @Author zfsn
 * @Date 2023/2/16 16:00
 */
@Service
public class CommonServiceImpl implements ICommonService {

	@Autowired
	private CommonMapper commonMapper;

	@Autowired
	private RemoteDictService remoteDictService;

	@Autowired
	private RemoteCommonInterfaceService remoteCommonInterfaceService;

	@Autowired
	private IScriptService scriptService;

	/**
	 * $模板解析工具
	 */
	private final PropertyPlaceholderHelper dollarHelper = new PropertyPlaceholderHelper("${", "}");

	/**
	 * &模板解析工具
	 */
	private final PropertyPlaceholderHelper isNotEmptyHelper = new PropertyPlaceholderHelper("#isNotEmpty{", "}");

	/**
	 * 校验具体请求参数的合法性
	 * @param req 具体请求参数
	 * @param validationRuleList 通用查询请求参数校验规则集
	 * @return 可能返回多种类型，如果是Boolean类型，为true时校验通过；如果类型为String，则表示校验不通过，值就用来提示给前端
	 */
	private Object validationReq(Map<String, Object> req, List<ValidationRule> validationRuleList) {
		for (ValidationRule validationRule : validationRuleList) {
			String paramName = validationRule.getParamName();
			Boolean required = validationRule.getRequired();
			String jsScript = validationRule.getJsScript();
			String message = StrUtil.isEmpty(validationRule.getMessage()) ? "请求参数" + paramName + "不合法！" : validationRule.getMessage();
			Object paramObj = req.get(paramName);
			// 如果有js脚本，则优先执行脚本
			if (StrUtil.isNotEmpty(jsScript)) {
				return scriptService.executeCommonValidation(jsScript, paramObj);
			}
			if (required && paramObj == null) {
				return message;
			}
			Integer min = validationRule.getMin();
			Integer max = validationRule.getMax();
			if (paramObj instanceof List) {
				// 集合类型校验
				List<?> param = (List<?>) paramObj;
				int size = param.size();
				if (required && size <= 0) {
					return message;
				}
				if (min != null && size < min) {
					return message;
				}
				if (max != null && size > max) {
					return message;
				}
			} else if (paramObj instanceof String) {
				// 字符串类型校验
				String param = (String) paramObj;
				int length = param.length();
				if (min != null && length < min) {
					return message;
				}
				if (max != null && length > max) {
					return message;
				}
				String regex = validationRule.getRegex();
				if (StrUtil.isNotEmpty(regex) && !ReUtil.isMatch(regex, param)) {
					return message;
				}
			} else if (paramObj instanceof Integer
					|| paramObj instanceof Long
					|| paramObj instanceof Float
					|| paramObj instanceof Double) {
				// 数值类型校验
				double param = Double.parseDouble(String.valueOf(paramObj));
				if (min != null && param < min) {
					return message;
				}
				if (max != null && param > max) {
					return message;
				}
			}
		}
		return Boolean.TRUE;
	}

	/**
	 * 数据格式化
	 * @param result 数据集
	 * @param conf 通用接口配置
	 */
	private void dataFormat(List<Map<String, Object>> result, CommonConf conf) {
		Map<String, String> dateFormatRuleMap = Optional.ofNullable(conf.getDateFormatRuleMap()).orElse(new HashMap<>());
		Map<String, String> timeFormatRuleMap = Optional.ofNullable(conf.getTimeFormatRuleMap()).orElse(new HashMap<>());
		Map<String, String> dictMappingRuleMap = Optional.ofNullable(conf.getDictMappingRuleMap()).orElse(new HashMap<>());
		if (CollUtil.isEmpty(dateFormatRuleMap) && CollUtil.isEmpty(timeFormatRuleMap) && CollUtil.isEmpty(dictMappingRuleMap)) {
			return;
		}
		for (Map<String, Object> record : result) {
			// 日期格式化
			dateFormatRuleMap.forEach(
					(field, format) -> Optional.ofNullable(record.get(field)).ifPresent(
							value -> record.put(field, LocalDateTimeUtil.format((LocalDateTime) value, DateTimeFormatter.ofPattern(format)))));
			// 时间格式化，统一默认保留两位小数
			timeFormatRuleMap.forEach(
					(field, formatType) -> Optional.ofNullable(record.get(field)).ifPresent(
							value -> record.put(field, TimeUtil.formatTime(formatType, value))));
			// 字典映射
			dictMappingRuleMap.forEach(
					(field, dictType) -> Optional.ofNullable(record.get(field)).ifPresent(value -> {
						List<SysDictItem> dictList = remoteDictService.getDictByType(dictType).getData();
						dictList.stream().filter(dict -> dict.getItemValue().equals(value)).findFirst().ifPresent(
								dict -> record.put(field, dict.getLabel()));
					}));
		}
	}

	/**
	 * 通用一般列表查询
	 * @param code 配置编码
	 * @param req 请求参数
	 * @return 通用返回对象
	 */
	@Override
	public R<List<Map<String, Object>>> list(String code, Map<String, Object> req) {
		// 1、根据配置编码获取对应的通用接口配置
		CommonConf conf = CommonConf.of(remoteCommonInterfaceService.queryByCode(code).getData().getDetail());

		// 2、校验参数合法性
		List<ValidationRule> validationRuleList = conf.getValidationRuleList();
		if (CollUtil.isNotEmpty(validationRuleList)) {
			Object validationResult = validationReq(req, validationRuleList);
			if (validationResult instanceof String) {
				return R.failed((String) validationResult);
			} else if (!(Boolean) validationResult) {
				return R.failed("请求参数校验不通过！");
			}
		}

		// 3、构建sql
		String tempSql = isNotEmptyHelper.replacePlaceholders(conf.getSqlTemplate(), new IsNotEmptyResolver(req));
		String sql = dollarHelper.replacePlaceholders(tempSql, new DollarResolver(req));

		// 4、执行查询
		List<Map<String, Object>> result = commonMapper.list(sql, conf.getBpmScope());

		// 5、字段格式化
		dataFormat(result, conf);

		// 6、返回结果
		return R.ok(result);
	}

	/**
	 * 通用分页列表查询
	 * @param page 分页参数
	 * @param code 配置编码
	 * @param req 请求参数
	 * @param <T> 分页对象类型
	 * @return 通用返回对象
	 */
	@Override
	public <T> R<Page<Map<String, Object>>> page(Page<T> page, String code, Map<String, Object> req) {
		// 1、根据配置编码获取对应的通用接口配置
		CommonConf conf = CommonConf.of(remoteCommonInterfaceService.queryByCode(code).getData().getDetail());

		// 2、校验参数合法性
		List<ValidationRule> validationRuleList = conf.getValidationRuleList();
		if (CollUtil.isNotEmpty(validationRuleList)) {
			Object validationResult = validationReq(req, validationRuleList);
			if (validationResult instanceof String) {
				return R.failed((String) validationResult);
			} else if (!(Boolean) validationResult) {
				return R.failed("请求参数校验不通过！");
			}
		}

		// 3、设置分页参数
		if (req.containsKey("current")) {
			page.setCurrent(Long.parseLong(String.valueOf(req.get("current"))));
		}
		if (req.containsKey("size")) {
			page.setSize(Long.parseLong(String.valueOf(req.get("size"))));
		}

		// 4、构建sql
		String tempSql = isNotEmptyHelper.replacePlaceholders(conf.getSqlTemplate(), new IsNotEmptyResolver(req));
		String sql = dollarHelper.replacePlaceholders(tempSql, new DollarResolver(req));

		// 5、执行查询
		Page<Map<String, Object>> result = commonMapper.page(page, sql, conf.getBpmScope());

		// 6、字段格式化
		dataFormat(result.getRecords(), conf);

		// 7、返回结果
		return R.ok(result);
	}

	/**
	 * 通用趋势图查询
	 * @param code 配置编码
	 * @param req 请求参数
	 * @return 通用返回对象
	 */
	@Override
	public R<Map<String, Object>> trendChart(String code, Map<String, Object> req) {
		// 1、根据配置编码获取对应的通用接口配置
		CommonConf conf = CommonConf.of(remoteCommonInterfaceService.queryByCode(code).getData().getDetail());

		// 2、校验参数合法性
		MergeDataConfig trendMergeConfig = conf.getTrendMergeConfig();
		if (trendMergeConfig == null) {
			return R.failed("趋势数据归并配置不存在！");
		}
		List<ValidationRule> validationRuleList = conf.getValidationRuleList();
		if (CollUtil.isNotEmpty(validationRuleList)) {
			Object validationResult = validationReq(req, validationRuleList);
			if (validationResult instanceof String) {
				return R.failed((String) validationResult);
			} else if (!(Boolean) validationResult) {
				return R.failed("请求参数校验不通过！");
			}
		}

		// 3、构建sql
		String tempSql = isNotEmptyHelper.replacePlaceholders(conf.getSqlTemplate(), new IsNotEmptyResolver(req));
		String sql = dollarHelper.replacePlaceholders(tempSql, new DollarResolver(req));

		// 4、执行查询
		List<Map<String, Object>> result = commonMapper.list(sql, conf.getBpmScope());

		// 5、字段格式化
		dataFormat(result, conf);

		// 6、趋势数据归并
		if (req.containsKey("scale")) {
			trendMergeConfig.setScale(Integer.parseInt(String.valueOf(req.get("scale"))));
		}
		trendMergeConfig.setStartTime(String.valueOf(req.get("startTime")));
		trendMergeConfig.setEndTime(String.valueOf(req.get("endTime")));
		trendMergeConfig.setData(result);
		Map<String, Object> mergeMap = MergeDataUtil.mergeData(trendMergeConfig);

		// 7、返回结果
		return R.ok(mergeMap);
	}

	/**
	 * 通用导出
	 * @param code 配置编码
	 * @param req 请求参数
	 * @param <T> vo类型
	 * @return 通用返回对象
	 */
	@Override
	public <T> R<T> export(String code, Map<String, Object> req) {
		return null;
	}
}
